/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 * The lipo(1) program.  This program creates, thins and operates on fat files.
 * This program takes the following options:
 *   <input_file>
 *   -output <filename>
 *   -create
 *   -info
 *   -detailed_info
 *   -arch <arch_type> <input_file>
 *   -arch_blank <arch_type>
 *   -thin <arch_type>
 *   -extract <arch_type>
 *   -remove <arch_type>
 *   -replace <arch_type> <file_name>
 *   -segalign <arch_type> <value>
 *   -verify_arch <arch_type> ...
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ar.h>
#ifndef AR_EFMT1
#define	AR_EFMT1	"#1/"		/* extended format #1 */
#endif
#include <limits.h>
#include <errno.h>
#include <ctype.h>
#include <libc.h>
#ifndef __OPENSTEP__
#include <utime.h>
#endif
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <mach/mach.h>
#include <mach-o/loader.h>
#include <mach-o/fat.h>
#include "stuff/arch.h"
#include "stuff/errors.h"
#include "stuff/allocate.h"
#include "stuff/lto.h"

#include "arch_info.h"
#include <cassert>


void raiseError(){
    throw 1;
}

//#define fatal(...) raiseError()
//#define error(...) raiseError()
//#define fatal(...) assert(0)
//#define error(...) assert(0)
#define fatal(...) throw 1 
#define error(...) throw 1



/* The maximum section alignment allowed to be specified, as a power of two */
#define MAXSECTALIGN		15 /* 2**15 or 0x8000 */


/* name of the program for error messages (argv[0]) */
char *progname = NULL;

/* The specified output file */
static bool archives_in_input = false;

/* flags set from command line arguments to specify the operation */


struct replace {
    struct arch_flag arch_flag;
    struct thin_file thin_file;
};

struct segalign {
    struct arch_flag arch_flag;
    uint32_t align;
};

static void process_replace_file(
                                 struct replace *replace);
void check_archive(
                          char *name,
                          char *addr,
                          uint32_t size,
                          cpu_type_t *cputype,
                          cpu_subtype_t *cpusubtype);
void check_extend_format_1(
                                  char *name,
                                  struct ar_hdr *ar_hdr,
                                  uint32_t size_left,
                                  uint32_t *member_name_size);
static uint32_t get_align(
                          struct mach_header *mhp,
                          struct load_command *load_commands,
                          uint32_t size,
                          char *name,
                          bool swapped);
static uint32_t get_align_64(
                             struct mach_header_64 *mhp64,
                             struct load_command *load_commands,
                             uint32_t size,
                             char *name,
                             bool swapped);
static uint32_t guess_align(
                            uint32_t vmaddr);
static int size_ar_name(
                        char *ar_name);
static struct input_file *new_input(
                                    void);
static struct segalign *new_segalign(
                                     void);

static uint32_t rnd(
                    uint32_t v,
                    uint32_t r);
static bool ispoweroftwo(
                              uint32_t x);
static void check_arch(
                       struct input_file *input,
                       struct thin_file *thin);


/*
 * process_input_file() checks input file and breaks it down into thin files
 * for later operations.
 */

void process_input_file(int fd, struct input_file *input, struct thin_file *thin)
{
    struct stat stat_buf, stat_buf2;
    uint32_t size, i, j;
    char *addr;
    struct mach_header *mhp, mh;
    struct mach_header_64 *mhp64, mh64;
    struct load_command *lcp;
    cpu_type_t cputype;
    cpu_subtype_t cpusubtype;
    bool swapped;
    uint64_t big_size;

	size = input->size;

	/*
	 * mmap() can't handle mapping regular files with zero size.  So this
	 * is handled separately.
	 */
	if((stat_buf.st_mode & S_IFREG) == S_IFREG && size == 0)
	    addr = NULL;
	else
	    addr = (char *) mmap(0, size, PROT_READ|PROT_WRITE, MAP_FILE|MAP_PRIVATE,
                    fd, 0);
	if((intptr_t)addr == -1)
	    fatal();
    
	/*
	 * Because of rdar://8087586 we do a second stat to see if the file
	 * is still there and the same file.
	 */
	if(fstat(fd, &stat_buf2) == -1)
	    fatal();
	if(stat_buf2.st_size != size ||
	   stat_buf2.st_mtime != stat_buf.st_mtime)
	    fatal();
    
    input->addr=addr;

    
	/* Try to figure out what kind of file this is */
    
	/* see if this file is a fat file */
	if(size >= sizeof(struct fat_header) &&
#ifdef __BIG_ENDIAN__
	   *((uint32_t *)addr) == FAT_MAGIC)
#endif /* __BIG_ENDIAN__ */
#ifdef __LITTLE_ENDIAN__
        *((uint32_t *)addr) == SWAP_INT(FAT_MAGIC))
#endif /* __LITTLE_ENDIAN__ */
	{
        
        
	    input->fat_header = (struct fat_header *)addr;
#ifdef __LITTLE_ENDIAN__
	    swap_fat_header(input->fat_header, LITTLE_ENDIAN_BYTE_SEX);
#endif /* __LITTLE_ENDIAN__ */
	    big_size = input->fat_header->nfat_arch;
	    big_size *= sizeof(struct fat_arch);
	    big_size += sizeof(struct fat_header);
	    if(big_size > size)
            fatal();
	    input->fat_arches = (struct fat_arch *)(addr +
                                                sizeof(struct fat_header));
#ifdef __LITTLE_ENDIAN__
	    swap_fat_arch(input->fat_arches, input->fat_header->nfat_arch,
                      LITTLE_ENDIAN_BYTE_SEX);
#endif /* __LITTLE_ENDIAN__ */
	    for(i = 0; i < input->fat_header->nfat_arch; i++){
            if(input->fat_arches[i].offset + input->fat_arches[i].size >
               size)
	    fatal();
            if(input->fat_arches[i].align > MAXSECTALIGN)
	    fatal();

            if(input->fat_arches[i].offset %
               (1 << input->fat_arches[i].align) != 0)
	    fatal();
	    }
	    for(i = 0; i < input->fat_header->nfat_arch; i++){
            for(j = i + 1; j < input->fat_header->nfat_arch; j++){
                if(input->fat_arches[i].cputype ==
                   input->fat_arches[j].cputype &&
                   (input->fat_arches[i].cpusubtype & ~CPU_SUBTYPE_MASK) ==
                   (input->fat_arches[j].cpusubtype & ~CPU_SUBTYPE_MASK))
	    fatal();
            }
	    }
        
	    /* create a thin file struct for each arch in the fat file */
	    for(i = 0; i < input->fat_header->nfat_arch; i++){
            thin->name = input->name;
            thin->addr = addr + input->fat_arches[i].offset;
            thin->fat_arch = input->fat_arches[i];
            thin->from_fat = true;
            if(input->fat_arches[i].size >= SARMAG &&
               strncmp(thin->addr, ARMAG, SARMAG) == 0)
                archives_in_input = true;
	    }
	}
	/* see if this file is Mach-O file for 32-bit architectures */
	else if(size >= sizeof(struct mach_header) &&
	        (*((uint32_t *)addr) == MH_MAGIC ||
	         *((uint32_t *)addr) == SWAP_INT(MH_MAGIC))){
                
                /* this is a Mach-O file so create a thin file struct for it */
                input->is_thin = true;
                thin->name = input->name;
                thin->addr = addr;
                mhp = (struct mach_header *)addr;
                lcp = (struct load_command *)((char *)mhp +
                                              sizeof(struct mach_header));
                if(mhp->magic == SWAP_INT(MH_MAGIC)){
                    swapped = true;
                    mh = *mhp;
                    swap_mach_header(&mh, get_host_byte_sex());
                    mhp = &mh;
                }
                else
                    swapped = false;
                thin->fat_arch.cputype = mhp->cputype;
                thin->fat_arch.cpusubtype = mhp->cpusubtype;
                thin->fat_arch.offset = 0;
                thin->fat_arch.size = size;
                thin->fat_arch.align = get_align(mhp, lcp, size, input->name,
                                                 swapped);
                
                /* if the arch type is specified make sure it matches the object */
                if(input->arch_flag.name != NULL)
                    check_arch(input, thin);
            }
	/* see if this file is Mach-O file for 64-bit architectures */
	else if(size >= sizeof(struct mach_header_64) &&
	        (*((uint32_t *)addr) == MH_MAGIC_64 ||
	         *((uint32_t *)addr) == SWAP_INT(MH_MAGIC_64))){
                
                /* this is a Mach-O file so create a thin file struct for it */
                input->is_thin = true;
                thin->name = input->name;
                thin->addr = addr;
                mhp64 = (struct mach_header_64 *)addr;
                lcp = (struct load_command *)((char *)mhp64 +
                                              sizeof(struct mach_header_64));
                if(mhp64->magic == SWAP_INT(MH_MAGIC_64)){
                    swapped = true;
                    mh64 = *mhp64;
                    swap_mach_header_64(&mh64, get_host_byte_sex());
                    mhp64 = &mh64;
                }
                else
                    swapped = false;
                thin->fat_arch.cputype = mhp64->cputype;
                thin->fat_arch.cpusubtype = mhp64->cpusubtype;
                thin->fat_arch.offset = 0;
                thin->fat_arch.size = size;
                thin->fat_arch.align = get_align_64(mhp64, lcp, size, input->name,
                                                    swapped);
                
                /* if the arch type is specified make sure it matches the object */
                if(input->arch_flag.name != NULL)
                    check_arch(input, thin);
            }
	/* see if this file is an archive file */
	else if(size >= SARMAG && strncmp(addr, ARMAG, SARMAG) == 0){
        
	    check_archive(input->name, addr, size, &cputype, &cpusubtype);
	    archives_in_input = true;
        
	    /* create a thin file struct for this archive */
	    thin->name = input->name;
	    thin->addr = addr;
	    thin->fat_arch.cputype = cputype;
	    thin->fat_arch.cpusubtype = cpusubtype;
	    thin->fat_arch.offset = 0;
	    thin->fat_arch.size = size;
	    thin->fat_arch.align = 2; /* 2^2, sizeof(uint32_t) */
        
	    /* if the arch type is specified make sure it matches the object */
	    if(input->arch_flag.name != NULL){
            if(cputype == 0){
                thin->fat_arch.cputype = input->arch_flag.cputype;
                thin->fat_arch.cpusubtype = input->arch_flag.cpusubtype;
            }
            else
                check_arch(input, thin);
	    }
	    else{
            if(cputype == 0)
	    fatal();
	    }
	}
	/* this file type is now known to be unknown to this program */
	else{
        
	    if(input->arch_flag.name != NULL){
            /* create a thin file struct for it */
            thin->name = input->name;
            thin->addr = addr;
            thin->fat_arch.cputype = input->arch_flag.cputype;
            thin->fat_arch.cpusubtype = input->arch_flag.cpusubtype;
            thin->fat_arch.offset = 0;
            thin->fat_arch.size = size;
            thin->fat_arch.align = 0;
	    }
	    else{
#ifdef LTO_SUPPORT
            if(is_llvm_bitcode_from_memory(addr, size, &input->arch_flag,
                                           NULL) != 0){
                /* create a thin file struct for it */
                thin->name = input->name;
                thin->addr = addr;
                thin->fat_arch.cputype = input->arch_flag.cputype;
                thin->fat_arch.cpusubtype = input->arch_flag.cpusubtype;
                thin->fat_arch.offset = 0;
                thin->fat_arch.size = size;
                thin->fat_arch.align = 0;
            }
            else
#endif /* LTO_SUPPORT */
	    fatal();
	    }
	}
}


/*
 * check_archive() checks an archive (mapped in at 'addr' of size 'size') to
 * make sure it can be included in a fat file (all members are the same
 * architecture, no fat files, etc).  It returns the cputype and cpusubtype.
 */
void 
check_archive(
              char *name,
              char *addr,
              uint32_t size,
              cpu_type_t *cputype,
              cpu_subtype_t *cpusubtype)
{
    uint32_t offset, magic, i, ar_name_size, ar_size;
    struct mach_header mh;
    struct mach_header_64 mh64;
    struct ar_hdr *ar_hdr;
    char *ar_name, *ar_addr;
    
	/*
	 * Check this archive out to make sure that it does not contain
	 * any fat files and that all object files it contains have the
	 * same cputype and subsubtype.
	 */
	*cputype = 0;
	*cpusubtype = 0;
	offset = SARMAG;
	if(offset == size)
	    fatal("empty archive with no architecture specification: %s "
              "(can't determine architecture for it)", name);
	if(offset != size && offset + sizeof(struct ar_hdr) > size)
	    fatal("truncated or malformed archive: %s (archive header of "
              "first member extends past the end of the file)", name);
	while(size > offset){
	    ar_hdr = (struct ar_hdr *)(addr + offset);
	    offset += sizeof(struct ar_hdr);
	    if(strncmp(ar_hdr->ar_name, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0){
            check_extend_format_1(name, ar_hdr, size-offset, &ar_name_size);
            i = ar_name_size;
            ar_name = ar_hdr->ar_name + sizeof(struct ar_hdr);
	    }
	    else{
            i = size_ar_name(ar_hdr->ar_name);
            ar_name = ar_hdr->ar_name;
            ar_name_size = 0;
	    }
	    if(size + ar_name_size - offset > sizeof(uint32_t)){
            memcpy(&magic, addr + offset + ar_name_size,
                   sizeof(uint32_t));
            if(magic == FAT_MAGIC)
                fatal("archive member %s(%.*s) is a fat file (not "
                      "allowed in an archive)", name, (int)i, ar_name);
            if((size - ar_name_size) - offset >=
               sizeof(struct mach_header) &&
               (magic == MH_MAGIC || magic == SWAP_INT(MH_MAGIC))){
                memcpy(&mh, addr + offset + ar_name_size,
                       sizeof(struct mach_header));
                if(mh.magic == SWAP_INT(MH_MAGIC))
                    swap_mach_header(&mh, get_host_byte_sex());
                if(*cputype == 0){
                    *cputype = mh.cputype;
                    *cpusubtype = mh.cpusubtype;
                }
                else if(*cputype != mh.cputype){
                    fatal("archive member %s(%.*s) cputype (%d) and "
                          "cpusubtype (%d) does not match previous "
                          "archive members cputype (%d) and cpusubtype"
                          " (%d) (all members must match)", name,
                          (int)i, ar_name, mh.cputype, mh.cpusubtype &
                          ~CPU_SUBTYPE_MASK, *cputype, (*cpusubtype) &
                          ~CPU_SUBTYPE_MASK);
                }
            }
            else if((size - ar_name_size) - offset >=
                    sizeof(struct mach_header_64) &&
                    (magic == MH_MAGIC_64 || magic == SWAP_INT(MH_MAGIC_64))){
                memcpy(&mh64, addr + offset + ar_name_size,
                       sizeof(struct mach_header_64));
                if(mh64.magic == SWAP_INT(MH_MAGIC_64))
                    swap_mach_header_64(&mh64, get_host_byte_sex());
                if(*cputype == 0){
                    *cputype = mh64.cputype;
                    *cpusubtype = mh64.cpusubtype;
                }
                else if(*cputype != mh64.cputype){
                    fatal("archive member %s(%.*s) cputype (%d) and "
                          "cpusubtype (%d) does not match previous "
                          "archive members cputype (%d) and cpusubtype"
                          " (%d) (all members must match)", name,
                          (int)i, ar_name, mh64.cputype, mh64.cpusubtype &
                          ~CPU_SUBTYPE_MASK, *cputype, (*cpusubtype) &
                          ~CPU_SUBTYPE_MASK);
                }
            }
            else{
                if(strncmp(ar_name, SYMDEF, sizeof(SYMDEF) - 1) != 0){
                    ar_addr = addr + offset + ar_name_size;
                    ar_size = strtoul(ar_hdr->ar_size, NULL, 10);
#ifdef LTO_SUPPORT
                    if(is_llvm_bitcode_from_memory(ar_addr, ar_size,
                                                   &arch_flag, NULL) != 0){
                        if(*cputype == 0){
                            *cputype = arch_flag.cputype;
                            *cpusubtype = arch_flag.cpusubtype;
                        }
                        else if(*cputype != arch_flag.cputype){
                            fatal("archive member %s(%.*s) cputype (%d) "
                                  "and cpusubtype (%d) does not match "
                                  "previous archive members cputype (%d) "
                                  "and cpusubtype (%d) (all members must "
                                  "match)", name, (int)i, ar_name,
                                  arch_flag.cputype, arch_flag.cpusubtype &
                                  ~CPU_SUBTYPE_MASK, *cputype,
                                  (*cpusubtype) & ~CPU_SUBTYPE_MASK);
                        }
                    }
#endif /* LTO_SUPPORT */
                }
            }
	    }
	    offset += rnd(strtoul(ar_hdr->ar_size, NULL, 10),
                      sizeof(short));
	}
}
/*
 * check_extend_format_1() checks the archive header for extened format1.
 */
void 
check_extend_format_1(
                      char *name,
                      struct ar_hdr *ar_hdr,
                      uint32_t size_left,
                      uint32_t *member_name_size)
{
    char *p, *endp, buf[sizeof(ar_hdr->ar_name)+1];
    uint32_t ar_name_size;
    
	*member_name_size = 0;
    
	buf[sizeof(ar_hdr->ar_name)] = '\0';
	memcpy(buf, ar_hdr->ar_name, sizeof(ar_hdr->ar_name));
	p = buf + sizeof(AR_EFMT1) - 1;
	if(isdigit(*p) == 0)
	    fatal("archive: %s malformed (ar_name: %.*s for archive extend "
              "format #1 starts with non-digit)", name,
              (int)sizeof(ar_hdr->ar_name), ar_hdr->ar_name);
	ar_name_size = strtoul(p, &endp, 10);
	if(ar_name_size == UINT_MAX && errno == ERANGE)
	    fatal("archive: %s malformed (size in ar_name: %.*s for archive "
              "extend format #1 overflows uint32_t)", name,
              (int)sizeof(ar_hdr->ar_name), ar_hdr->ar_name);
	while(*endp == ' ' && *endp != '\0')
	    endp++;
	if(*endp != '\0')
	    fatal("archive: %s malformed (size in ar_name: %.*s for archive "
              "extend format #1 contains non-digit and non-space "
              "characters)", name, (int)sizeof(ar_hdr->ar_name),
              ar_hdr->ar_name);
	if(ar_name_size > size_left)
	    fatal("archive: %s truncated or malformed (archive name "
              "of member extends past the end of the file)", name);
	*member_name_size = ar_name_size;
}

/*
 * get_align is passed a pointer to a mach header and size of the object.  It
 * returns the segment alignment the object was created with.  It guesses but
 * it is conservative.  The maximum alignment is that the link editor will allow
 * MAXSECTALIGN and the minimum is the conserative alignment for a uint32_t
 * which appears in a mach object files (2^2 worst case for all current 32-bit
 * machines).
 */
static
uint32_t
get_align(
          struct mach_header *mhp,
          struct load_command *load_commands,
          uint32_t size,
          char *name,
          bool swapped)
{
    uint32_t i, j, cur_align, align;
    struct load_command *lcp, l;
    struct segment_command *sgp, sg;
    struct section *sp, s;
    enum byte_sex host_byte_sex;
    
	/*
	 * Special case ppc, ppc64, i386 and x86_64 architectures and return 12.
	 * We know that with those architectures that the kernel and mmap only
	 * need file offsets to be page (4096 byte) aligned.
	 */
	if(mhp->cputype == CPU_TYPE_POWERPC ||
	   mhp->cputype == CPU_TYPE_POWERPC64 ||
	   mhp->cputype == CPU_TYPE_I386 ||
	   mhp->cputype == CPU_TYPE_X86_64)
	    return(12);
    
	host_byte_sex = get_host_byte_sex();
    
	/* set worst case the link editor uses first */
	cur_align = MAXSECTALIGN;
	if(mhp->sizeofcmds + sizeof(struct mach_header) > size)
	    fatal("truncated or malformed object (load commands would "
              "extend past the end of the file) in: %s", name);
	lcp = load_commands;
	for(i = 0; i < mhp->ncmds; i++){
	    l = *lcp;
	    if(swapped)
            swap_load_command(&l, host_byte_sex);
	    if(l.cmdsize % sizeof(uint32_t) != 0)
            error("load command %u size not a multiple of "
                  "sizeof(uint32_t) in: %s", i, name);
	    if(l.cmdsize <= 0)
            fatal("load command %u size is less than or equal to zero "
                  "in: %s", i, name);
	    if((char *)lcp + l.cmdsize >
	       (char *)load_commands + mhp->sizeofcmds)
            fatal("load command %u extends past end of all load "
                  "commands in: %s", i, name);
	    if(l.cmd == LC_SEGMENT){
            sgp = (struct segment_command *)lcp;
            sg = *sgp;
            if(swapped)
                swap_segment_command(&sg, host_byte_sex);
            if(mhp->filetype == MH_OBJECT){
                /* this is the minimum alignment, then take largest */
                align = 2; /* 2^2 sizeof(uint32_t) */
                sp = (struct section *)((char *)sgp +
                                        sizeof(struct segment_command));
                for(j = 0; j < sg.nsects; j++){
                    s = *sp;
                    if(swapped)
                        swap_section(&s, 1, host_byte_sex);
                    if(s.align > align)
                        align = s.align;
                    sp++;
                }
                if(align < cur_align)
                    cur_align = align;
            }
            else{
                /* guess the smallest alignment and use that */
                align = guess_align(sg.vmaddr);
                if(align < cur_align)
                    cur_align = align;
            }
	    }
	    lcp = (struct load_command *)((char *)lcp + l.cmdsize);
	}
	return(cur_align);
}

/*
 * get_align_64 is passed a pointer to a mach_header_64 and size of the object.
 * It returns the segment alignment the object was created with.  It guesses but
 * it is conservative.  The maximum alignment is that the link editor will allow
 * MAXSECTALIGN and the minimum is the conserative alignment for a long long
 * which appears in a mach object files (2^3 worst case for all 64-bit
 * machines).
 */
static
uint32_t
get_align_64(
             struct mach_header_64 *mhp64,
             struct load_command *load_commands,
             uint32_t size,
             char *name,
             bool swapped)
{
    uint32_t i, j, cur_align, align;
    struct load_command *lcp, l;
    struct segment_command_64 *sgp, sg;
    struct section_64 *sp, s;
    enum byte_sex host_byte_sex;
    
	/*
	 * Special case ppc, ppc64, i386 and x86_64 architectures and return 12.
	 * We know that with those architectures that the kernel and mmap only
	 * need file offsets to be page (4096 byte) aligned.
	 */
	if(mhp64->cputype == CPU_TYPE_POWERPC ||
	   mhp64->cputype == CPU_TYPE_POWERPC64 ||
	   mhp64->cputype == CPU_TYPE_I386 ||
	   mhp64->cputype == CPU_TYPE_X86_64)
	    return(12);
    
	host_byte_sex = get_host_byte_sex();
    
	/* set worst case the link editor uses first */
	cur_align = MAXSECTALIGN;
	if(mhp64->sizeofcmds + sizeof(struct mach_header_64) > size)
	    fatal("truncated or malformed object (load commands would "
              "extend past the end of the file) in: %s", name);
	lcp = load_commands;
	for(i = 0; i < mhp64->ncmds; i++){
	    l = *lcp;
	    if(swapped)
            swap_load_command(&l, host_byte_sex);
	    if(l.cmdsize % sizeof(long long) != 0)
            error("load command %u size not a multiple of "
                  "sizeof(long long) in: %s", i, name);
	    if(l.cmdsize <= 0)
            fatal("load command %u size is less than or equal to zero "
                  "in: %s", i, name);
	    if((char *)lcp + l.cmdsize >
	       (char *)load_commands + mhp64->sizeofcmds)
            fatal("load command %u extends past end of all load "
                  "commands in: %s", i, name);
	    if(l.cmd == LC_SEGMENT_64){
            sgp = (struct segment_command_64 *)lcp;
            sg = *sgp;
            if(swapped)
                swap_segment_command_64(&sg, host_byte_sex);
            if(mhp64->filetype == MH_OBJECT){
                /* this is the minimum alignment, then take largest */
                align = 3; /* 2^3 sizeof(long long) */
                sp = (struct section_64 *)((char *)sgp +
                                           sizeof(struct segment_command_64));
                for(j = 0; j < sg.nsects; j++){
                    s = *sp;
                    if(swapped)
                        swap_section_64(&s, 1, host_byte_sex);
                    if(s.align > align)
                        align = s.align;
                    sp++;
                }
                if(align < cur_align)
                    cur_align = align;
            }
            else{
                /* guess the smallest alignment and use that */
                align = guess_align(sg.vmaddr);
                if(align < cur_align)
                    cur_align = align;
            }
	    }
	    lcp = (struct load_command *)((char *)lcp + l.cmdsize);
	}
	return(cur_align);
}

/*
 * guess_align is passed a vmaddr of a segment and guesses what the segment
 * alignment was.  It uses the most conservative guess up to the maximum
 * alignment that the link editor uses.
 */
static
uint32_t
guess_align(
            uint32_t vmaddr)
{
    uint32_t align, segalign;
    
	if(vmaddr == 0)
	    return(MAXSECTALIGN);
    
	align = 0;
	segalign = 1;
	while((segalign & vmaddr) == 0){
	    segalign = segalign << 1;
	    align++;
	}
	
	if(align < 2)
	    return(2);
	if(align > MAXSECTALIGN)
	    return(MAXSECTALIGN);
    
	return(align);
}

/*
 * size_ar_name is used to return the size of the name of an archive member
 * for printing it without blanks (with printf string "%.*s").
 */
static
int
size_ar_name(
             char *ar_name)
{
    uint32_t j;
    struct ar_hdr ar_hdr;
    
	for(j = 0; j < sizeof(ar_hdr.ar_name); j++){
	    if(ar_name[j] == ' ')
            break;
	}
	return(j);
}





/*
 * rnd() rounds v to a multiple of r.
 */
static
uint32_t
rnd(
    uint32_t v,
    uint32_t r)
{
	r--;
	v += r;
	v &= ~(int32_t)r;
	return(v);
}

/*
 * ispoweroftwo() returns true or false depending if x is a power of two.
 */
static
bool
ispoweroftwo(
             uint32_t x)
{
	if(x == 0)
	    return(true);
	while((x & 0x1) != 0x1){
	    x >>= 1;
	}
	if((x & ~0x1) != 0)
	    return(false);
	else
	    return(true);
}

/*
 * check_arch is called when an input file is specified with a -arch flag input
 * and that the architecture can be determined from the input to check that
 * both architectures match.
 */
static
void
check_arch(
           struct input_file *input,
           struct thin_file *thin)
{
	if(input->arch_flag.cputype != thin->fat_arch.cputype)
	    fatal("specifed architecture type (%s) for file (%s) does "
              "not match its cputype (%d) and cpusubtype (%d) "
              "(should be cputype (%d) and cpusubtype (%d))",
              input->arch_flag.name, input->name,
              thin->fat_arch.cputype, thin->fat_arch.cpusubtype &
              ~CPU_SUBTYPE_MASK, input->arch_flag.cputype,
              input->arch_flag.cpusubtype & ~CPU_SUBTYPE_MASK);
}


